package megrez.oss.local.components;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

import javax.annotation.PostConstruct;

import org.apache.tika.Tika;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import megrez.oss.common.FileEmptyException;
import megrez.oss.common.FileNameException;
import megrez.oss.common.FileNameHelper;
import megrez.oss.common.OSSException;
import megrez.oss.common.Pair;
import megrez.oss.local.config.LocalProperties;
import megrez.oss.local.domain.FilePathPair;
import megrez.oss.local.domain.Metadata;
import megrez.oss.local.domain.MetadataRepository;
import megrez.oss.local.utils.FileExistException;
import megrez.oss.local.utils.FileNotFoundExcepiton;
import megrez.oss.local.utils.FileUnReadableException;

/**
 * 本地存储组件
 * 
 * @author Lucky Yang
 * @since 2.6.9
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class StorageComponent {
    private final LocalProperties properties;
    private final MetadataRepository metadataRepo;

    private Path uploadRootPath;
    private static final Tika TITA = new Tika();

    @PostConstruct
    public void init() throws IOException {
        uploadRootPath = properties.getUploadRootPath();

        if (!Files.exists(uploadRootPath)) {
            Files.createDirectories(uploadRootPath);
        }

        log.info("Initialized upload root path successfully  [{}]", uploadRootPath);
    }

    /**
     * 存储文件
     * 
     * @param file 文件
     * @return 元数据
     */
    @Transactional(rollbackFor = Exception.class)
    public Metadata store(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new FileEmptyException();
        }

        try {
            String fileName = file.getOriginalFilename();
            if (!FileNameHelper.validate(fileName)) {
                throw new FileNameException();
            }

            // 根据文件名，生成两级目录
            final Pair<String, String> filePath = FileNameHelper.generalHashPath(fileName);
            final Path destinationPath = this.uploadRootPath.resolve(filePath.value0)
                    .resolve(filePath.value1);
            if (!Files.exists(destinationPath)) {
                Files.createDirectories(destinationPath);
            }

            // 存储文件元数据
            final Metadata metadata = new Metadata();
            metadata.setContentType(TITA.detect(file.getInputStream()));
            metadata.setPath(FilePathPair.withPair(filePath));
            metadata.setSize(file.getSize());
            metadata.setName(fileName);
            metadataRepo.save(metadata);

            if (log.isDebugEnabled()) {
                log.debug("Store file [{}]", metadata);
            }

            final Path fileFullPath = destinationPath.resolve(String.valueOf(metadata.getId()));
            if (Files.exists(fileFullPath)) {
                throw new FileExistException(fileFullPath.toString());
            }

            try (InputStream fileData = file.getInputStream()) {
                // 存储文件
                Files.copy(fileData, fileFullPath, StandardCopyOption.REPLACE_EXISTING);
                return metadata;
            }

        } catch (IOException e) {
            throw new OSSException(e);
        }
    }

    /**
     * 下载文件
     * 
     * @param metadata 文件元数据
     * @return
     */
    public FileData download(Metadata metadata) {
        if (log.isDebugEnabled()) {
            log.debug("Download [{}]", metadata);
        }

        try {
            Path fileFullPath = this.uploadRootPath.resolve(metadata.getPath().getFirst())
                    .resolve(metadata.getPath().getSecond())
                    .resolve(String.valueOf(metadata.getId()));

            Resource file = new UrlResource(fileFullPath.toUri());

            if (!file.exists()) {
                throw new FileNotFoundExcepiton(metadata.toString());
            }

            if (!file.isReadable()) {
                throw new FileUnReadableException(metadata.getName());
            }

            return new FileData(metadata, file);
        } catch (MalformedURLException e) {
            throw new OSSException(e);
        }
    }

    /**
     * 删除文件
     * 
     * @throws IOException
     */
    @Transactional(rollbackFor = Exception.class)
    public void remove(Metadata metadata) {
        if (log.isDebugEnabled()) {
            log.debug("Remove file [{}]", metadata);
        }

        metadataRepo.delete(metadata);
        // 删除文件
        Path fileFullPath = this.uploadRootPath.resolve(metadata.getPath().getFirst()).resolve(metadata.getPath().getSecond())
                .resolve(String.valueOf(metadata.getId()));
        try {
            Files.deleteIfExists(fileFullPath);
        } catch (IOException e) {
           log.warn("file delete exception: {}", e.getMessage());
        }

    }
}
